package com.zhangxu.microservice.system.biz.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.zhangxu.microservice.common.utils.AssertUtils;
import com.zhangxu.microservice.common.utils.ListUtils;
import com.zhangxu.microservice.system.biz.convert.ResourceConverter;
import com.zhangxu.microservice.system.biz.dto.ApplicationDTO;
import com.zhangxu.microservice.system.biz.dto.BatchUploadResourceTreeDTO;
import com.zhangxu.microservice.system.biz.dto.ResourceDTO;
import com.zhangxu.microservice.system.biz.dto.ResourceTreeDTO;
import com.zhangxu.microservice.system.biz.service.ApplicationService;
import com.zhangxu.microservice.system.biz.service.ResourceService;
import com.zhangxu.microservice.system.dao.entity.ResourceDO;
import com.zhangxu.microservice.system.dao.service.ResourceDao;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 资源服务实现类
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class ResourceServiceImpl implements ResourceService {

    private final ResourceDao resourceDao;
    private final ResourceConverter resourceConverter;
    private final ApplicationService applicationService;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchUploadResourceTree(BatchUploadResourceTreeDTO uploadTreeDTO) {
        String appCode = uploadTreeDTO.getAppCode();
        ApplicationDTO applicationDTO = applicationService.getApplicationByCode(appCode);
        AssertUtils.notNull(applicationDTO, "系统编码不存在");
        // 先删除该系统下所有资源
        List<ResourceDO> existingResources = resourceDao.getByAppId(applicationDTO.getId());
        if (!existingResources.isEmpty()) {
            List<Long> resourceIds = ListUtils.mapList(existingResources, ResourceDO::getId);
            resourceDao.removeByIds(resourceIds);
        }

        // 递归处理资源树并保存
        List<ResourceDO> resourceDOList = new ArrayList<>();
        saveResourceTree(uploadTreeDTO.getResources(), resourceDOList, "");
        //统一设置applicationCode
        resourceDOList.forEach(resourceDO -> resourceDO.setAppId(applicationDTO.getId()));
        CollUtil.reverse(resourceDOList);
        resourceDao.saveBatch(resourceDOList);
    }

    /**
     * 递归保存资源树
     *
     * @param resourceTreeList   资源树列表
     * @param resourceDOList     系统编码
     * @param parentResourceCode 父资源编码
     * @return 保存的资源数量
     */
    private void saveResourceTree(List<ResourceTreeDTO> resourceTreeList, List<ResourceDO> resourceDOList, String parentResourceCode) {
        for (ResourceTreeDTO resourceTree : resourceTreeList) {
            // 创建资源
            ResourceDO resourceDO = new ResourceDO();
            resourceDO.setAppId(resourceTree.getAppId());
            resourceDO.setCode(resourceTree.getCode());
            resourceDO.setParentCode(parentResourceCode);
            resourceDO.setName(resourceTree.getName());
            resourceDO.setType(resourceTree.getType());
            resourceDO.setPath(resourceTree.getPath());
            resourceDO.setComponent(resourceTree.getComponent());
            resourceDO.setMethod(resourceTree.getMethod());
            // 递归处理子资源
            if (resourceTree.getChildren() != null && !resourceTree.getChildren().isEmpty()) {
                saveResourceTree(resourceTree.getChildren(), resourceDOList, resourceTree.getCode());
            }
            resourceDOList.add(resourceDO);
        }
    }

    @Override
    public List<ResourceTreeDTO> getResourceTree(Long appId) {
        // 获取系统下所有资源
        List<ResourceDO> allResources = resourceDao.getByAppId(appId);

        // 转换为DTO
        List<ResourceDTO> resourceDTOList = allResources.stream()
                .map(resourceConverter::toResourceDTO)
                .collect(Collectors.toList());

        // 构建资源树
        return buildResourceTree(resourceDTOList);
    }

    /**
     * 构建资源树
     *
     * @param resourceDTOList 资源DTO列表
     * @return 资源树列表（顶级资源）
     */
    private List<ResourceTreeDTO> buildResourceTree(List<ResourceDTO> resourceDTOList) {
        List<ResourceTreeDTO> result = new ArrayList<>();
        Map<String, ResourceTreeDTO> resourceMap = new HashMap<>();

        // 转换为ResourceTreeDTO并建立映射关系
        for (ResourceDTO resourceDTO : resourceDTOList) {
            ResourceTreeDTO treeNode = convertToTreeDTO(resourceDTO);
            resourceMap.put(resourceDTO.getCode(), treeNode);
        }

        // 构建树形结构
        for (ResourceDTO resourceDTO : resourceDTOList) {
            String parentCode = resourceDTO.getParentCode();
            if (parentCode == null || parentCode.isEmpty()) {
                // 顶级资源
                result.add(resourceMap.get(resourceDTO.getCode()));
            } else {
                // 子资源，添加到父资源的children列表中
                ResourceTreeDTO parentNode = resourceMap.get(parentCode);
                if (parentNode != null) {
                    if (parentNode.getChildren() == null) {
                        parentNode.setChildren(new ArrayList<>());
                    }
                    parentNode.getChildren().add(resourceMap.get(resourceDTO.getCode()));
                }
            }
        }

        return result;
    }

    /**
     * 将ResourceDTO转换为ResourceTreeDTO
     *
     * @param resourceDTO 资源DTO
     * @return 资源树DTO
     */
    private ResourceTreeDTO convertToTreeDTO(ResourceDTO resourceDTO) {
        ResourceTreeDTO treeDTO = new ResourceTreeDTO();
        treeDTO.setId(resourceDTO.getId());
        treeDTO.setAppId(resourceDTO.getAppId());
        treeDTO.setCode(resourceDTO.getCode());
        treeDTO.setParentCode(resourceDTO.getParentCode());
        treeDTO.setName(resourceDTO.getName());
        treeDTO.setType(resourceDTO.getType());
        treeDTO.setPath(resourceDTO.getPath());
        treeDTO.setMethod(resourceDTO.getMethod());
        treeDTO.setComponent(resourceDTO.getComponent());
        return treeDTO;
    }
}